package com.szsh.aiot.hsm.utils;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;

public class AESUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(AESUtil.class);
    // 偏移量
    private static int offset = 16;
    private static String transformation = "AES/CBC/PKCS5Padding";
    private static String algorithm = "AES";
    private static String EncryptDesc = "Encrypt Failed";
    private static String DecryptDesc = "Decrypt Failed";
    private static final String ALGO_MODE = "AES/CBC/NoPadding";

    /**
     * AES（128）加密
     *
     * @param content
     *            需要加密的内容
     * @param base64Key
     *            key
     * @param ivStr
     * @return 加密内容
     */
    public static String encrypt(String content, String base64Key, String ivStr) {
        try {
            SecretKeySpec skey = new SecretKeySpec(Base64Utils.decodeFromString(base64Key), algorithm);
            IvParameterSpec iv = new IvParameterSpec(ivStr.getBytes(StandardCharsets.UTF_8), 0, offset);
            Cipher cipher = Cipher.getInstance(transformation);
            byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
            cipher.init(Cipher.ENCRYPT_MODE, skey, iv);
            byte[] result = cipher.doFinal(byteContent);
            return Base64Utils.encodeToString(result);
        } catch (Exception e) {
            throw new RuntimeException(EncryptDesc, e);
        }
    }

    /**
     * AES（128）解密
     *
     * @param content
     *            待解密内容
     * @param base64Key
     *            key
     * @param ivStr
     * @return 解密后内容
     */
    public static String decrypt(String content, String base64Key, String ivStr) {
        try {
            SecretKeySpec skey = new SecretKeySpec(Base64Utils.decodeFromString(base64Key), algorithm);
            IvParameterSpec iv = new IvParameterSpec(ivStr.getBytes(StandardCharsets.UTF_8), 0, offset);
            Cipher cipher = Cipher.getInstance(transformation);
            cipher.init(Cipher.DECRYPT_MODE, skey, iv);
            byte[] result = cipher.doFinal(Base64Utils.decodeFromString(content));
            return new String(result);
        } catch (Exception e) {
            throw new RuntimeException(DecryptDesc, e);
        }
    }

    /**
     * 指定偏移量byte的aes加密
     * @param sSrc
     * @param sKey
     * @param ivByte
     * @return
     */
    public static String encryptByIvByte(String sSrc, String sKey, byte[] ivByte) {
        try {
            if (sKey == null) {
                LOGGER.info("Key为空null");
                return null;
            }
            // 判断Key是否为16位
            if (sKey.length() != 16) {
                LOGGER.info("Key长度不是16位");
                return null;
            }
            byte[] raw = sKey.getBytes();
            SecretKeySpec skeySpec = new SecretKeySpec(raw, algorithm);
            //"算法/模式/补码方式"
            Cipher cipher = Cipher.getInstance(transformation);
            //使用CBC模式，需要一个向量iv，可增加加密算法的强度
            IvParameterSpec iv = new IvParameterSpec(ivByte);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(sSrc.getBytes());
            return Base64.encodeBase64String(encrypted);
        } catch (Exception e) {
            throw new RuntimeException(EncryptDesc, e);
        }
    }

    /**
     * 指定偏移量byte的aes解密
     * @param sSrc
     * @param sKey
     * @param ivByte
     * @return
     */
    public static String decryptByIvByte(String sSrc, String sKey, byte[] ivByte) {
        try {
            // 判断Key是否正确
            if (sKey == null) {
                LOGGER.info("Key为空null");
                return null;
            }
            // 判断Key是否为16位
            if (sKey.length() != 16) {
                LOGGER.info("Key长度不是16位");
                return null;
            }
            byte[] raw = sKey.getBytes("ASCII");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, algorithm);
            Cipher cipher = Cipher.getInstance(transformation);
            IvParameterSpec iv = new IvParameterSpec(ivByte);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            //先用base64解密
            byte[] encrypted1 = Base64.decodeBase64(sSrc);
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString;
        } catch (Exception e) {
            throw new RuntimeException(DecryptDesc, e);
        }
    }


    /**
     * AES（128）解密
     *
     * @param content
     *            待解密内容
     * @param base64Key
     *            key
     * @param ivStr
     * @return 解密后内容
     */
    public static String urlSafeDecrypt(String content, String base64Key, String ivStr) {
        try {
            SecretKeySpec skey = new SecretKeySpec(Base64Utils.decodeFromString(base64Key), algorithm);
            IvParameterSpec iv = new IvParameterSpec(ivStr.getBytes(StandardCharsets.UTF_8), 0, offset);
            Cipher cipher = Cipher.getInstance(transformation);
            cipher.init(Cipher.DECRYPT_MODE, skey, iv);
            byte[] result = cipher.doFinal(Base64Utils.decodeFromUrlSafeString(content));
            return new String(result);
        } catch (Exception e) {
            throw new RuntimeException(DecryptDesc, e);
        }
    }

    /**
     * 生成128位base64编码后的key
     *
     * @return key
     */
    public static String createRandomBase64Key() {
        try {
            KeyGenerator kg = KeyGenerator.getInstance(algorithm);
            SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
            sr.setSeed(SecureRandom.getInstance("SHA1PRNG").generateSeed(128));
            kg.init(128, sr);
            SecretKey sk = kg.generateKey();
            byte[] b = sk.getEncoded();
            return Base64Utils.encodeToString(b);
        } catch (Exception e) {
            throw new RuntimeException("create key Failed  ", e);
        }
    }

    /**
     * 适配 AES/CBC/PKCS7Padding 的解密
     * 不需要额外引入第三方类库 org.bouncycastle:bcprov-jdk15on:1.65
     * @param data
     * @param key
     * @param aiv
     * @return
     * @throws Exception
     */
    public static String decryptCbcWithIv(String data, String key, String aiv) throws Exception {
        byte[] encrypted1 = Hex.sHex2byte(data);
        byte[] iv = aiv.getBytes();
        Cipher cipher = Cipher.getInstance(ALGO_MODE);
        SecretKeySpec keyspec = new SecretKeySpec(key.getBytes("utf-8"), algorithm);
        IvParameterSpec ivspec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
        byte[] original = cipher.doFinal(encrypted1);
        String originalString = new String(original);
        return originalString.trim();
    }

  /**
   * 适配 AES/CBC/PKCS7Padding 的加密
   * 不需要额外引入第三方类库 org.bouncycastle:bcprov-jdk15on:1.65
   * @param data
   * @param key
   * @param aiv
   * @return
   * @throws Exception
   */
  public static String encryptCbcWithIv(String data, String key, String aiv) throws Exception {
        String rstData = pkcs7padding(data);
        byte[] iv = aiv.getBytes();
        Cipher cipher = Cipher.getInstance(ALGO_MODE);
        int blockSize = cipher.getBlockSize();
        byte[] dataBytes = rstData.getBytes();
        int plaintextLength = dataBytes.length;
        if (plaintextLength % blockSize != 0) {
            plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
        }
        byte[] plaintext = new byte[plaintextLength];
        System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
        SecretKeySpec keyspec = new SecretKeySpec(key.getBytes("utf-8"), algorithm);
        IvParameterSpec ivspec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
        byte[] encrypted = cipher.doFinal(plaintext);
        return Hex.byte2HexString(encrypted);
    }

    /**
     * pkcs7padding填充函数
     *
     * @param data
     * @return
     */
    public static String pkcs7padding(String data) {
        int bs = 16;
        int padding = bs - (data.length() % bs);
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < padding; i++) {
            buf.append((char) padding);
        }
        return data + buf.toString();
    }

    /**
     * 无偏移量密钥加密
     * @param src
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptNoIv(String src, String key) throws Exception {
        // 判断密钥是否为空
        if (key == null) {
            LOGGER.info("Key不能为空");
            return null;
        }
        // 密钥补位
        byte[] data = key.getBytes("utf-8");
        byte[] raw = new byte[16];
        byte[] plusbyte = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
        for (int i = 0; i < 16; i++) {
            if (data.length > i)
                raw[i] = data[i];
            else
                raw[i] = plusbyte[i];
        }
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        // 算法/模式/补码方式
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(src.getBytes("utf-8"));
        return Hex.byte2HexString(encrypted);
    }

    public static void main(String[] args) {
        String str = "%E6%B2%A1%E6%9C%89%E9%9C%80%E8%A6%81%E9%80%80%E8%AE%A2%E7%9A%84%E8%AE%A2%E5%8D%95%21";
        System.out.println(AESUtil.decryptNoIv(str.toLowerCase(), "123QWE$%^"));
    }
    /**
     * 无偏移量密钥解密
     *
     * @param src 解密字符串
     * @param key 密钥
     * @return 解密后的字符串
     */
    public static String decryptNoIv(String src, String key) {
        try {
            // 判断Key是否正确
            if (key == null) {
                LOGGER.info("Key不能为空");
                return null;
            }
            // 密钥补位
            byte[] data = key.getBytes("utf-8");
            byte[] raw = new byte[16];
            byte[] plusbyte = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
            for (int i = 0; i < 16; i++) {
                if (data.length > i)
                    raw[i] = data[i];
                else
                    raw[i] = plusbyte[i];
            }
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] encrypted1 = Hex.sHex2byte(src);
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original, "utf-8");
                return originalString;
            } catch (Exception e) {
                return null;
            }
        } catch (Exception ex) {
            return null;
        }
    }

}
